/*****************************************************************************
 *   Copyright(C)2009-2022 by VSF Team                                       *
 *                                                                           *
 *  Licensed under the Apache License, Version 2.0 (the "License");          *
 *  you may not use this file except in compliance with the License.         *
 *  You may obtain a copy of the License at                                  *
 *                                                                           *
 *     http://www.apache.org/licenses/LICENSE-2.0                            *
 *                                                                           *
 *  Unless required by applicable law or agreed to in writing, software      *
 *  distributed under the License is distributed on an "AS IS" BASIS,        *
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 *  See the License for the specific language governing permissions and      *
 *  limitations under the License.                                           *
 *                                                                           *
 ****************************************************************************/

/*============================ INCLUDES ======================================*/

#include "hal/vsf_hal_cfg.h"

#if VSF_HAL_USE_USART == ENABLED

#include "hal/vsf_hal.h"
#include "HME_MCU.h"
#include "hme_uart.h"

/*============================ MACROS ========================================*/

#ifndef VSF_HW_USART_CFG_MULTI_CLASS
#   define VSF_HW_USART_CFG_MULTI_CLASS         VSF_USART_CFG_MULTI_CLASS
#endif

#define VSF_USART_CFG_IMP_PREFIX                vsf_hw
#define VSF_USART_CFG_IMP_UPCASE_PREFIX         VSF_HW

// TODO: check hardware fifo size
#define __HW_USART_FIFO_SIZE                    16

#ifndef VSF_HW_CFG_USART_PROTECT_LEVEL
#   define VSF_HW_CFG_USART_PROTECT_LEVEL       interrupt
#endif

#define vsf_hw_usart_protect                    vsf_protect(VSF_HW_CFG_USART_PROTECT_LEVEL)
#define vsf_hw_usart_unprotect                  vsf_unprotect(VSF_HW_CFG_USART_PROTECT_LEVEL)

/*============================ MACROFIED FUNCTIONS ===========================*/
/*============================ TYPES =========================================*/

typedef struct VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) {
#if VSF_HW_USART_CFG_MULTI_CLASS == ENABLED
    vsf_usart_t                 vsf_usart;
#endif
    IRQn_Type                   irqn;
    UART_Type                  *reg;
    vsf_usart_isr_t             isr;

    struct {
        uint8_t                 tx_line;
        uint8_t                 rx_line;
        int8_t                  tx_chn;
        int8_t                  rx_chn;
        
        vsf_usart_irq_mask_t    irq_mask;
        uint32_t                rx_count;
        uint32_t                tx_count;
    } dma;
} VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t);

/*============================ INCLUDES ======================================*/
/*============================ GLOBAL VARIABLES ==============================*/
/*============================ LOCAL VARIABLES ===============================*/
/*============================ PROTOTYPES ====================================*/
/*============================ IMPLEMENTATION ================================*/

vsf_err_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_init)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr,
    vsf_usart_cfg_t *cfg_ptr
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);
    UART_Type *uart = usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != uart);
    VSF_HAL_ASSERT(NULL != cfg_ptr);

    uint32_t pclk = GetPeripheralCLOCK();
    uint32_t div = pclk / cfg_ptr->baudrate;
    if ((div & 0xFFFF00) == 0) {
        //VSF_HAL_ASSERT(0);
        return VSF_ERR_FAIL;
    }

    uart->FCR = UART_FCR_RFIFOR_MSK | UART_FCR_XFIFOR_MSK;  // reset fifo
    uart->IER = 0;                                          // disable all interrupt in usart
    uart->MCR = (cfg_ptr->mode & DWC_USART_MCR_MODE_ALL_BITS_MASK) >> __DWC_USART_MCR_OFFSET;
    uart->LCR = UART_LCR_DLAB_MSK | (cfg_ptr->mode & DWC_USART_FCR_MODE_ALL_BITS_MASK);
    // Baud-Rate-Divisor = pclk / (16 * Baud-Rate)
    // Integer part of Baud-Rate-Divisor save  to (DLH * 16 + DLL)
    // Fractional part of Baud-Rate-Divisor * 16 save  to DLF
    uart->DLH = (div & 0x0FF000) >> 12;
    uart->DLL = (div & 0x000FF0) >> 4;
    uart->DLF = div & 0x00F;
    uart->LCR &= ~UART_LCR_DLAB_MSK;
    // uart fifo: rx and tx trigger 1 byte;
    uart->FCR = UART_RxFIFOTrigger_1_CHARACTER |
                UART_TxFIFOTrigger_FIFO_EMPTY |
                UART_DMA_MODE0 |
                UART_FCR_FIFOE_MSK |
                UART_FCR_RFIFOR_MSK |
                UART_FCR_XFIFOR_MSK;

    vsf_usart_isr_t *isr_ptr = &cfg_ptr->isr;
    usart_ptr->isr = *isr_ptr;
    if (isr_ptr->handler_fn != NULL) {
        NVIC_SetPriority(usart_ptr->irqn, (uint32_t)isr_ptr->prio);
        NVIC_EnableIRQ(usart_ptr->irqn);
    } else {
        NVIC_DisableIRQ(usart_ptr->irqn);
    }
    
    return VSF_ERR_NONE;
}

void VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_fini)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);
    UART_Type *uart = usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != uart);    
    
    NVIC_DisableIRQ(usart_ptr->irqn);
}

fsm_rt_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_enable)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr
) {
    // hardware always enable
    return fsm_rt_cpl;
}

fsm_rt_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_disable)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr
) {
    //vsf_hw_usart_cancel_rx(usart_ptr);
    //vsf_hw_usart_cancel_tx(usart_ptr);

    return fsm_rt_cpl;
}

void VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_irq_enable)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr,
    vsf_usart_irq_mask_t irq_mask
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);
    UART_Type *uart = usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != uart);
    
    irq_mask &= DWC_USART_IRQ_ALL_BITS_MASK | DWC_USART_IRQ_ALL_DMA_BITS_MASK;

    vsf_usart_irq_mask_t hw_irq_mask = irq_mask & DWC_USART_IRQ_ALL_BITS_MASK;
    if (hw_irq_mask) {
        uart->IER |= hw_irq_mask;
    }

    vsf_usart_irq_mask_t dma_irq_mask = irq_mask & DWC_USART_IRQ_ALL_DMA_BITS_MASK;
    if (dma_irq_mask) {
        usart_ptr->dma.irq_mask |= dma_irq_mask;
    }
}

void VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_irq_disable)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr,
    vsf_usart_irq_mask_t irq_mask
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);
    UART_Type *uart = usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != uart);

    irq_mask &= (DWC_USART_IRQ_ALL_BITS_MASK | DWC_USART_IRQ_ALL_DMA_BITS_MASK);

    vsf_usart_irq_mask_t hw_irq_mask = irq_mask & DWC_USART_IRQ_ALL_BITS_MASK;
    if (hw_irq_mask) {
        uart->IER &= ~hw_irq_mask;
    }

    vsf_usart_irq_mask_t dma_irq_mask = irq_mask & DWC_USART_IRQ_ALL_DMA_BITS_MASK;
    if (dma_irq_mask) {
        usart_ptr->dma.irq_mask &= ~dma_irq_mask;
    }
}

static bool __hw_usart_rx_fifo_is_empty(UART_Type *uart)
{
    // Data Ready bit
    return !uart->LSR_b.DR;
}

static uint32_t __hw_usart_rx_fifo_read(UART_Type *uart)
{
    return uart->RBR;
}

static bool __hw_usart_write_fifo_is_empty(UART_Type *uart)
{
    // Transmitter Shift Register and the FIFO are both empty.
    // hardware only support get tx fifo empty status
    //return !!uart->LSR_b.TEMT;
    return !!uart->LSR_b.THRE;
}

static void __hw_usart_tx_fifo_write(UART_Type *uart, uint32_t data)
{
    uart->THR = data;
}

vsf_usart_status_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_status)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);
    UART_Type *uart = usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != uart);
    
    uint8_t rx_is_busy = !__hw_usart_rx_fifo_is_empty(uart);
    uint8_t tx_is_busy = !__hw_usart_write_fifo_is_empty(uart);

    vsf_usart_status_t status = {
        .is_busy = rx_is_busy || tx_is_busy,
    };

    return status;
}

uint_fast16_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_rxfifo_get_data_count)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);
    UART_Type *uart = usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != uart);

    // TODO: how to read fifo current size ?
    return __hw_usart_rx_fifo_is_empty(uart) ? 0 : __HW_USART_FIFO_SIZE;
}

uint_fast32_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_rxfifo_read)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr,
    void *buffer_ptr,
    uint_fast16_t count
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);
    VSF_HAL_ASSERT(NULL != buffer_ptr);

    UART_Type *uart = usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != uart);

    uint8_t *buf_ptr = (uint8_t *) buffer_ptr;
    VSF_HAL_ASSERT(NULL != buf_ptr);

    uint_fast16_t i = 0;
    while (i < count) {
        if (__hw_usart_rx_fifo_is_empty(uart)) {
            break;
        }
        buf_ptr[i++] = __hw_usart_rx_fifo_read(uart);
    }
    return i;
}

uint_fast16_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_txfifo_get_free_count)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);
    UART_Type *uart = usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != uart);

    return __hw_usart_write_fifo_is_empty(uart) ? __HW_USART_FIFO_SIZE : 0;
}

uint_fast32_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_txfifo_write)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr,
    void *buffer_ptr,
    uint_fast16_t count
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);
    UART_Type *uart = usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != uart);

    uint8_t *buf_ptr = (uint8_t *) buffer_ptr;
    VSF_HAL_ASSERT(NULL != buf_ptr);
   
    if (__hw_usart_write_fifo_is_empty(uart)) {
        count = vsf_min(count, __HW_USART_FIFO_SIZE);
        for (int i = 0; i < count; i++) {
            __hw_usart_tx_fifo_write(uart, buf_ptr[i]);
        }
        return count;
    } else {
        // maybe we can write some data, but we don't know current fifo remain size
        // TODO: find best way
        return 0;
    }
}

vsf_usart_capability_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_capability)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);

    uint32_t pclk = GetPeripheralCLOCK();

    vsf_usart_capability_t usart_capability = {
        .irq_mask                       = DWC_USART_IRQ_ALL_BITS_MASK | DWC_USART_IRQ_ALL_DMA_BITS_MASK,
        .max_baudrate                   = pclk / (16 * 1),
        .min_baudrate                   = pclk / (16 * 1) / 65535,
        .max_data_bits                  = 8,
        .min_data_bits                  = 5,
        .txfifo_depth                   = __HW_USART_FIFO_SIZE,
        .rxfifo_depth                   = __HW_USART_FIFO_SIZE,
        .support_rx_timeout             = 0,
        .support_send_break             = 0,
        .support_set_and_clear_break    = 1,
    };

    return usart_capability;
}

static vsf_usart_irq_mask_t __get_uart_irq_mask(vsf_hw_usart_t *usart_ptr)
{
    VSF_HAL_ASSERT(NULL != usart_ptr);
    UART_Type *uart = usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != uart);

    uint32_t iir_value = uart->IIR & UART_IIR_MASK;
    uint32_t dump;
    
    switch (iir_value) {
        case UART_IIR_RECEIVED_LINE_STATUS: // Overrun/parity/ framing errors, break interrupt, oraddress received interrupt
            return (uart->LSR << DWC_USART_IRQ_LINE_STATUS_OFFSET) & VSF_USART_IRQ_MASK_ERR;

        case UART_IIR_RECEIVED_DATA_AVAILABLE:
            return VSF_USART_IRQ_MASK_RX;

        // No characters in or out of the RCVR FIFO during the last 4 character times 
        // and there isat least 1 character in it during this time
        case UART_IIR_CHARACTER_TIMEOUT:
            dump = __hw_usart_rx_fifo_read(uart);
            (void)dump;
            return 0;
        
        case UART_IIR_THR_EMPTY:
            return VSF_USART_IRQ_MASK_TX;

        case UART_IIR_MODEM_STATUS: {
                uint32_t msr_value = uart->MSR;        // TODO: report more specific moden line interrupt
                (void)msr_value;
                return (vsf_usart_irq_mask_t)0;
            }

        case UART_IIR_BUSY_DETECT: {
                uint32_t lsr_value = uart->LSR;        // TODO: Check why the re-initialization will enter this interrupt 
                (void)lsr_value;
                return (vsf_usart_irq_mask_t)0;
            }

        default:
            VSF_HAL_ASSERT(0);
            return (vsf_usart_irq_mask_t)0;
    }
}

static void VSF_MCONNECT(__, VSF_USART_CFG_IMP_PREFIX, _usart_irqhandler)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);

    vsf_usart_irq_mask_t irq_mask = __get_uart_irq_mask(usart_ptr);
    vsf_usart_isr_t *isr_ptr = &usart_ptr->isr;
    if ((irq_mask != 0) && (isr_ptr->handler_fn != NULL)) {
        isr_ptr->handler_fn(isr_ptr->target_ptr, (vsf_usart_t *)usart_ptr, irq_mask);
    }
}

static void __request_rx_dma_callback(void *target_ptr, vsf_dma_t *dma_ptr,
                                      int8_t channel, vsf_dma_irq_mask_t irq_mask)
{
    vsf_hw_usart_t *usart_ptr = (vsf_hw_usart_t *)target_ptr;
    VSF_HAL_ASSERT(NULL != usart_ptr);

    if (irq_mask & VSF_DMA_IRQ_MASK_CPL) {
        if ((usart_ptr->dma.irq_mask & VSF_USART_IRQ_MASK_RX_CPL) && (NULL != usart_ptr->isr.handler_fn)) {
            usart_ptr->isr.handler_fn(usart_ptr->isr.target_ptr, (vsf_usart_t *)usart_ptr, VSF_USART_IRQ_MASK_RX_CPL);
        }
    }
}

vsf_err_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_request_rx)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr,
    void *buffer_ptr,
    uint_fast32_t count
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);
    UART_Type *uart = usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != uart);
    
    if (usart_ptr->dma.rx_line < 0) {
        return VSF_ERR_NOT_SUPPORT;
    }
    
    if (usart_ptr->dma.rx_chn < 0) {
        int8_t chn = vsf_hw_dma_channel_request(&vsf_hw_dma0);
        if (chn < 0) {
            return VSF_ERR_FAIL;
        } else {
            usart_ptr->dma.rx_chn = chn;
        }
        
        // TODO: request dma channel
        vsf_dma_channel_mode_t mode = VSF_DMA_PERIPHERA_TO_MEMORY
            | VSF_DMA_SRC_ADDR_NO_CHANGE | VSF_DMA_DST_ADDR_INCREMENT
            | VSF_DMA_SRC_BURST_LENGTH_1 | VSF_DMA_DST_BURST_LENGTH_1
            | VSF_DMA_SRC_WIDTH_BYTE_1   | VSF_DMA_DST_WIDTH_BYTE_1;
        vsf_dma_channel_cfg_t cfg = {
            .mode = mode,
            .src_idx = usart_ptr->dma.rx_line,
            .dst_idx = 0,
            .isr  = {
                .handler_fn = __request_rx_dma_callback,
                .target_ptr = usart_ptr,
            },
        };
        
        vsf_err_t err = vsf_hw_dma_channel_config(&vsf_hw_dma0, chn, &cfg);
        if (err != VSF_ERR_NONE) {
            return err;
        }
    }
    
    return vsf_hw_dma_channel_start(&vsf_hw_dma0, usart_ptr->dma.rx_chn, (uint32_t)&uart->RBR, (uint32_t)buffer_ptr, count);
}

static void __request_tx_dma_callback(void *target_ptr, vsf_dma_t *dma_ptr,
                                      int8_t channel, vsf_dma_irq_mask_t irq_mask)
{
    vsf_hw_usart_t *usart_ptr = (vsf_hw_usart_t *)target_ptr;
    VSF_HAL_ASSERT(NULL != usart_ptr);

    if (irq_mask & VSF_DMA_IRQ_MASK_CPL) {
        if ((usart_ptr->dma.irq_mask & VSF_USART_IRQ_MASK_TX_CPL) && (NULL != usart_ptr->isr.handler_fn)) {
            usart_ptr->isr.handler_fn(usart_ptr->isr.target_ptr, (vsf_usart_t *)usart_ptr, VSF_USART_IRQ_MASK_TX_CPL);
        }
    }
}

vsf_err_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_request_tx)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr,
    void *buffer_ptr,
    uint_fast32_t count
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);
    UART_Type *uart = usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != uart);
    
    if (usart_ptr->dma.tx_line < 0) {
        return VSF_ERR_NOT_SUPPORT;
    }
    
    if (usart_ptr->dma.tx_chn < 0) {
        int8_t chn = vsf_hw_dma_channel_request(&vsf_hw_dma0);
        if (chn < 0) {
            return VSF_ERR_FAIL;
        } else {
            usart_ptr->dma.tx_chn = chn;
        }
        
        vsf_dma_channel_mode_t mode = VSF_DMA_MEMORY_TO_PERIPHERAL
            | VSF_DMA_SRC_ADDR_INCREMENT | VSF_DMA_DST_ADDR_NO_CHANGE
            | VSF_DMA_SRC_BURST_LENGTH_1 | VSF_DMA_DST_BURST_LENGTH_1
            | VSF_DMA_SRC_WIDTH_BYTE_1   | VSF_DMA_DST_WIDTH_BYTE_1;
        vsf_dma_channel_cfg_t cfg = {
            .mode = mode,
            .src_idx = 0,
            .dst_idx = usart_ptr->dma.tx_line,
            .isr  = {
                .handler_fn = __request_tx_dma_callback,
                .target_ptr = usart_ptr,
            },
        };
        
        vsf_err_t err = vsf_hw_dma_channel_config(&vsf_hw_dma0, chn, &cfg);
        if (err != VSF_ERR_NONE) {
            return err;
        }
    }

    return vsf_hw_dma_channel_start(&vsf_hw_dma0,
        usart_ptr->dma.tx_chn, (uint32_t)buffer_ptr, (uint32_t)&uart->THR, count);
}

vsf_err_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_cancel_rx)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);

    if (usart_ptr->dma.tx_line < 0) {
        return VSF_ERR_NOT_SUPPORT;
    }
    
    if (usart_ptr->dma.rx_chn < 0) {
        return VSF_ERR_FAIL;
    }
    
    int8_t dma_rx_chn = usart_ptr->dma.rx_chn;
    usart_ptr->dma.rx_chn = -1;
    
    vsf_hw_dma_channel_cancel(&vsf_hw_dma0, dma_rx_chn);    
    usart_ptr->dma.rx_count = vsf_hw_dma_channel_get_transferred_count(&vsf_hw_dma0, dma_rx_chn);
    vsf_hw_dma_channel_release(&vsf_hw_dma0, dma_rx_chn);
    
    return VSF_ERR_NONE;
}

vsf_err_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_cancel_tx)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);

    if (usart_ptr->dma.tx_line < 0) {
        return VSF_ERR_NOT_SUPPORT;
    }
    
    if (usart_ptr->dma.tx_chn < 0) {
        return VSF_ERR_FAIL;
    }
    
    int8_t tx_dma_chn = usart_ptr->dma.tx_chn;
    usart_ptr->dma.tx_chn = -1;
    
    vsf_hw_dma_channel_cancel(&vsf_hw_dma0, tx_dma_chn);    
    usart_ptr->dma.tx_count = vsf_hw_dma_channel_get_transferred_count(&vsf_hw_dma0, tx_dma_chn);
    vsf_hw_dma_channel_release(&vsf_hw_dma0, tx_dma_chn);
    
    return VSF_ERR_NONE;
}

int_fast32_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_get_rx_count)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);
        
    if (usart_ptr->dma.rx_line < 0) {
        VSF_HAL_ASSERT(0);
        return -1;
    }

    return usart_ptr->dma.rx_count;
}

int_fast32_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_get_tx_count)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);

    if (usart_ptr->dma.tx_line < 0) {
        VSF_HAL_ASSERT(0);    
        return -1;
    }
    
    return usart_ptr->dma.tx_count;
}

vsf_err_t VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_cmd)(
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t) *usart_ptr,
    vsf_usart_cmd_t cmd,
    void * param
) {
    VSF_HAL_ASSERT(NULL != usart_ptr);
    UART_Type *uart = usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != uart);
    vsf_err_t err = VSF_ERR_NOT_SUPPORT;
    
    switch (cmd) {
    case VSF_USART_CMD_SET_BREAK:
        uart->LCR |= UART_LCR_BC_MSK;
        err = VSF_ERR_NONE;
        break;
    case VSF_USART_CMD_CLEAR_BREAK:
        uart->LCR &= ~UART_LCR_BC_MSK;
        err = VSF_ERR_NONE;
        break;
    default:
        VSF_HAL_ASSERT(0);
        break;
    }
    
    return err;
}

/*============================ MACROFIED FUNCTIONS ===========================*/

#define VSF_USART_CFG_REIMPLEMENT_API_CAPABILITY                ENABLED
#define VSF_USART_CFG_REIMPLEMENT_API_REQUEST                   ENABLED
#define VSF_USART_CFG_REIMPLEMENT_API_CMD                       ENABLED
#define VSF_USART_CFG_IMP_LV0(__IDX, __HAL_OP)                                  \
    VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart_t)                            \
        VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart, __IDX) = {               \
        .reg  = VSF_MCONNECT(VSF_USART_CFG_IMP_UPCASE_PREFIX, _USART,           \
                             __IDX, _REG),                                      \
        .irqn = VSF_MCONNECT(VSF_USART_CFG_IMP_UPCASE_PREFIX, _USART,           \
                             __IDX, _IRQN),                                     \
        .dma  = {                                                               \
            .tx_chn  = -1,                                                      \
            .rx_chn  = -1,                                                      \
            .tx_line = VSF_MCONNECT(VSF_USART_CFG_IMP_UPCASE_PREFIX, _USART,    \
                                    __IDX, _DMA_TX_CHN),                        \
            .rx_line = VSF_MCONNECT(VSF_USART_CFG_IMP_UPCASE_PREFIX, _USART,    \
                                    __IDX, _DMA_RX_CHN),                        \
        },                                                                      \
        __HAL_OP                                                                \
    };                                                                          \
    void VSF_MCONNECT(VSF_USART_CFG_IMP_UPCASE_PREFIX, _USART, __IDX,           \
                      _IRQHandler)(void)                                        \
    {                                                                           \
        uintptr_t ctx = vsf_hal_irq_enter();                                    \
        VSF_MCONNECT(__, VSF_USART_CFG_IMP_PREFIX, _usart_irqhandler)(          \
            &VSF_MCONNECT(VSF_USART_CFG_IMP_PREFIX, _usart, __IDX)              \
        );                                                                      \
        vsf_hal_irq_leave(ctx);                                                 \
    }
    
#include "hal/driver/common/usart/usart_template.inc"

#endif /* VSF_HAL_USE_USART */
